# -*- coding:UTF-8 -*-

# region 导入必要的依赖包
import os
import json
from datetime import datetime
import copy
import hashlib
import re

try:
    from git import Repo, Remote  # 需要安装 GitPython 模块，以支持git操作
except ImportError as impErr:
    print("尝试导入 git 依赖时检测到异常：", impErr)
    print("尝试安装 GitPython 模块：")
    try:
        os.system("pip install GitPython")
    except OSError as osErr:
        print("尝试安装模块 GitPython 时检测到异常：", osErr)
        exit(0)
    else:
        try:
            # 如果模块安装成功，则再次尝试导入依赖
            from git import Repo, Remote
        except Exception as expErr:
            print("再次尝试导入 git 依赖时检测到异常：", expErr)
            exit(0)

try:
    import pymysql  # 需要安装 pymysql 模块，以支持 mysql 操作
except ImportError as impErr:
    print("尝试导入 pymysql 依赖时检测到异常：", impErr)
    print("尝试安装 pymysql 模块：")
    try:
        os.system("pip install pymysql")
    except OSError as osErr:
        print("尝试安装模块 pymysql 时检测到异常：", osErr)
        exit(0)
    else:
        try:
            # 如果模块安装成功，则再次尝试导入依赖
            import pymysql
        except Exception as expErr:
            print("再次尝试导入 pymysql 依赖时检测到异常：", expErr)
            exit(0)

try:
    from DebugInfo.DebugInfo import *
except ImportError as impErr:
    print("尝试导入 DebugInfo 依赖时检测到异常：", impErr)
    try:
        os.system("pip install DebugInfo")
    except OSError as osErr:
        print("尝试安装模块 DebugInfo 时检测到异常：", osErr)
        exit(0)
    else:
        try:
            # 如果模块安装成功，则再次尝试导入依赖
            from DebugInfo.DebugInfo import *
        except Exception as expErr:
            print("再次尝试导入 DebugInfo 依赖时检测到异常：", expErr)
            exit(0)

# endregion

路径分隔符: str = '\\' if 在nt系统中() else '/'

class 命令行参数类(入参基类):
    def __init__(self):
        # 初始化父类
        super().__init__()

        # 添加个性化参数
        self._添加参数('productId', str, '指定 productId 值')
        self._添加参数('majorBranchName', str, '指定 majorBranchName 值', 'master')
        self._添加参数('tagPrefix_major', str, '指定 tagPrefix_major 值', 'm')
        self._添加参数('minorBranchName', str, '指定 minorBranchName 值', 'release')
        self._添加参数('tagPrefix_minor', str, '指定 tagPrefix_minor 值', 'r')
        self._添加参数('gitRePoDir', str, '指定 git 仓库的路径')
        self._添加参数('gitVerSql_host', str, '如果你希望向mysql上传记录,请指定 Sql_host 值')
        self._添加参数('gitVerSql_user', str, '如果你希望向mysql上传记录,请指定 Sql_user 值')
        self._添加参数('gitVerSql_pwd', str, '如果你希望向mysql上传记录,请指定 Sql_password 值')
        self._添加参数('gitVerSql_charset', str, '如果你希望向mysql上传记录,请指定 Sql_charset 值', 'utf8')
        self._添加参数('gitVerSql_table', str, '如果你希望向mysql上传记录,请指定 Sql_table 值')
        self._添加参数('gitVerSql_db', str, '如果你希望向mysql上传记录,请指定 Sql_database 值')
        self._添加参数('focusDoc_fileName', str, '如果你希望特别关注某文件/文档的变动,请指定文档名', '')

    # region 访问器
    @property
    def productId(self) -> str:
        if 'productId' in self._参数字典:
            return self._参数字典['productId'].值
        else:
            return ''

    @productId.setter
    def productId(self, 值: str):
        if 'productId' in self._参数字典:
            if isinstance(值, str):
                self._参数字典['productId'].值 = 值

    @property
    def majorBranchName(self) -> str:
        if 'majorBranchName' in self._参数字典:
            return self._参数字典['majorBranchName'].值
        else:
            return ''

    @majorBranchName.setter
    def majorBranchName(self, 值: str):
        if 'majorBranchName' in self._参数字典:
            if isinstance(值, str):
                self._参数字典['majorBranchName'].值 = 值

    @property
    def tagPrefix_major(self) -> str:
        if 'tagPrefix_major' in self._参数字典:
            return self._参数字典['tagPrefix_major'].值
        else:
            return ''

    @tagPrefix_major.setter
    def tagPrefix_major(self, 值: str):
        if 'tagPrefix_major' in self._参数字典:
            if isinstance(值, str):
                self._参数字典['tagPrefix_major'].值 = 值

    @property
    def minorBranchName(self) -> str:
        if 'minorBranchName' in self._参数字典:
            return self._参数字典['minorBranchName'].值
        else:
            return ''

    @minorBranchName.setter
    def minorBranchName(self, 值: str):
        if 'minorBranchName' in self._参数字典:
            if isinstance(值, str):
                self._参数字典['minorBranchName'].值 = 值

    @property
    def tagPrefix_minor(self) -> str:
        if 'tagPrefix_minor' in self._参数字典:
            return self._参数字典['tagPrefix_minor'].值
        else:
            return ''

    @tagPrefix_minor.setter
    def tagPrefix_minor(self, 值: str):
        if 'tagPrefix_minor' in self._参数字典:
            if isinstance(值, str):
                self._参数字典['tagPrefix_minor'].值 = 值

    @property
    def gitRePoDir(self) -> str:
        if 'gitRePoDir' in self._参数字典:
            return self._参数字典['gitRePoDir'].值
        else:
            return ''

    @gitRePoDir.setter
    def gitRePoDir(self, 值: str):
        if 'gitRePoDir' in self._参数字典:
            if isinstance(值, str):
                self._参数字典['gitRePoDir'].值 = 值

    @property
    def gitVerSql_host(self) -> str:
        if 'gitVerSql_host' in self._参数字典:
            return self._参数字典['gitVerSql_host'].值
        else:
            return ''

    @gitVerSql_host.setter
    def gitVerSql_host(self, 值: str):
        if 'gitVerSql_host' in self._参数字典:
            if isinstance(值, str):
                self._参数字典['gitVerSql_host'].值 = 值

    @property
    def gitVerSql_user(self) -> str:
        if 'gitVerSql_user' in self._参数字典:
            return self._参数字典['gitVerSql_user'].值
        else:
            return ''

    @gitVerSql_user.setter
    def gitVerSql_user(self, 值: str):
        if 'gitVerSql_user' in self._参数字典:
            if isinstance(值, str):
                self._参数字典['gitVerSql_user'].值 = 值

    @property
    def gitVerSql_pwd(self) -> str:
        if 'gitVerSql_pwd' in self._参数字典:
            return self._参数字典['gitVerSql_pwd'].值
        else:
            return ''

    @gitVerSql_pwd.setter
    def gitVerSql_pwd(self, 值: str):
        if 'gitVerSql_pwd' in self._参数字典:
            if isinstance(值, str):
                self._参数字典['gitVerSql_pwd'].值 = 值

    @property
    def gitVerSql_charset(self) -> str:
        if 'gitVerSql_charset' in self._参数字典:
            return self._参数字典['gitVerSql_charset'].值
        else:
            return ''

    @gitVerSql_charset.setter
    def gitVerSql_charset(self, 值: str):
        if 'gitVerSql_charset' in self._参数字典:
            if isinstance(值, str):
                self._参数字典['gitVerSql_charset'].值 = 值

    @property
    def gitVerSql_table(self) -> str:
        if 'gitVerSql_table' in self._参数字典:
            return self._参数字典['gitVerSql_table'].值
        else:
            return ''

    @gitVerSql_table.setter
    def gitVerSql_table(self, 值: str):
        if 'gitVerSql_table' in self._参数字典:
            if isinstance(值, str):
                self._参数字典['gitVerSql_table'].值 = 值

    @property
    def gitVerSql_db(self) -> str:
        if 'gitVerSql_db' in self._参数字典:
            return self._参数字典['gitVerSql_db'].值
        else:
            return ''

    @gitVerSql_db.setter
    def gitVerSql_db(self, 值: str):
        if 'gitVerSql_db' in self._参数字典:
            if isinstance(值, str):
                self._参数字典['gitVerSql_db'].值 = 值

    @property
    def focusDoc_fileName(self) -> str:
        if 'focusDoc_fileName' in self._参数字典:
            return self._参数字典['focusDoc_fileName'].值
        else:
            return ''

    @focusDoc_fileName.setter
    def focusDoc_fileName(self, 值: str):
        if 'focusDoc_fileName' in self._参数字典:
            if isinstance(值, str):
                self._参数字典['focusDoc_fileName'].值 = 值
    # endregion


# 定义一个类，用于管理 MySql 配置信息
class MySqlCfg:
    def __init__(self,
                 host: str = '',
                 user: str = '',
                 pwd: str = '',
                 db: str = '',
                 charset: str = '',
                 table: str = ''):
        self.host: str = host
        self.user: str = user
        self.pwd: str = pwd
        self.db: str = db
        self.charset: str = charset
        self.table: str = table

    @property
    def valid(self) -> bool:
        if self.host and self.user and self.pwd and self.db and self.charset and self.table:
            return True
        else:
            return False

    def readJson(self,
                 mySqlCfgDir: str = '',
                 白板: 调试模板 = 调试模板()) -> bool:
        白板.执行位置(self.__class__, self.readJson)

        readRstFlg: bool = False
        if not os.path.isfile(mySqlCfgDir):
            白板.调试消息('mySqlCfgDir is not a file')
        else:
            if not os.path.exists(mySqlCfgDir):
                白板.调试消息('gitVerInfoCfgDir is not exists')
            else:
                with open(mySqlCfgDir, 'r', encoding='utf-8') as f:
                    mySqlCfgFromJson = json.load(f)

                mySqlCfgFromJsonSub = None
                if not mySqlCfgFromJson:
                    白板.调试消息('mySqlCfgFromJson is None')
                elif "gitVerSql_host" in mySqlCfgFromJson:
                    mySqlCfgFromJsonSub = mySqlCfgFromJson
                elif "mySql" in mySqlCfgFromJson:
                    mySqlCfgFromJsonSub = mySqlCfgFromJson["mySql"]

                if not mySqlCfgFromJsonSub:
                    白板.调试消息('mySqlCfg is None')
                else:
                    if "host" in mySqlCfgFromJsonSub:
                        self.host = mySqlCfgFromJsonSub["host"]
                    if "user" in mySqlCfgFromJsonSub:
                        self.user = mySqlCfgFromJsonSub["user"]
                    if "pwd" in mySqlCfgFromJsonSub:
                        self.pwd = mySqlCfgFromJsonSub["pwd"]
                    if "db" in mySqlCfgFromJsonSub:
                        self.db = mySqlCfgFromJsonSub["db"]
                    if "table" in mySqlCfgFromJsonSub:
                        self.table = mySqlCfgFromJsonSub["table"]
                    if "charset" in mySqlCfgFromJsonSub:
                        self.charset = mySqlCfgFromJsonSub["charset"]

                    readRstFlg = True

        return readRstFlg

    def showInfo(self, 白板: 调试模板 = 调试模板()):
        白板.执行位置(self.__class__, self.showInfo)

        白板.准备表格()
        白板.添加一行('host:', self.host)
        白板.添加一行('user:', self.user)
        白板.添加一行('pwd:', self.pwd)
        白板.添加一行('db:', self.db)
        白板.添加一行('table:', self.table)
        白板.添加一行('charset:', self.charset)
        白板.展示表格()

    @property
    def 副本(self):
        副本 = MySqlCfg()

        副本.host = self.host
        副本.user = self.user
        副本.pwd = self.pwd
        副本.db = self.db
        副本.table = self.table
        副本.charset = self.charset

        return 副本


# 定义一个类，用于管理 gitVerInfo 的配置信息
class GitVerInfoCfg:
    def __init__(self,
                 gitRePoDir: str = '',
                 productId: str = 'xxxxx',
                 majorBranchName: str = 'master',
                 tagPrefix_major: str = 'm',
                 minorBranchName: str = 'release',
                 tagPrefix_minor: str = 'r',
                 anchorChar: str = '',
                 focusDoc_fileName: str = '',
                 mySqlCfg: MySqlCfg = None):
        self.gitRePoDir: str = gitRePoDir
        self.productId: str = productId
        self.majorBranchName: str = majorBranchName
        self.tagPrefix_major: str = tagPrefix_major
        self.minorBranchName: str = minorBranchName
        self.tagPrefix_minor: str = tagPrefix_minor
        self.anchorChar: str = anchorChar
        self.focusDoc_fileName: str = focusDoc_fileName
        self.mySqlCfg = mySqlCfg if mySqlCfg else MySqlCfg()

    def readJson(self,
                 gitVerInfoCfgDir: str = '',
                 白板: 调试模板 = 调试模板()) -> bool:
        白板.执行位置(self.__class__, self.readJson)

        readRstFlg: bool = False
        if not os.path.isfile(gitVerInfoCfgDir):
            白板.调试消息('gitVerInfoCfgDir is not a file')
        else:
            if not os.path.exists(gitVerInfoCfgDir):
                白板.调试消息('gitVerInfoCfgDir is not exists')
            else:
                with open(gitVerInfoCfgDir, 'r', encoding='utf-8') as f:
                    gitVerInfoCfgFromJson = json.load(f)

                if not gitVerInfoCfgFromJson:
                    白板.调试消息('gitVerInfoCfgFromJson is None')
                else:
                    if "productId" in gitVerInfoCfgFromJson:
                        self.productId = gitVerInfoCfgFromJson["productId"]
                    if "majorBranchName" in gitVerInfoCfgFromJson:
                        self.majorBranchName = gitVerInfoCfgFromJson["majorBranchName"]
                    if "tagPrefix_major" in gitVerInfoCfgFromJson:
                        self.tagPrefix_major = gitVerInfoCfgFromJson["tagPrefix_major"]
                    if "minorBranchName" in gitVerInfoCfgFromJson:
                        self.minorBranchName = gitVerInfoCfgFromJson["minorBranchName"]
                    if "tagPrefix_minor" in gitVerInfoCfgFromJson:
                        self.tagPrefix_minor = gitVerInfoCfgFromJson["tagPrefix_minor"]
                    if "anchorChar" in gitVerInfoCfgFromJson:
                        self.anchorChar = gitVerInfoCfgFromJson["anchorChar"]

                    mySqlCfgFromJson = None
                    if "mySql" in gitVerInfoCfgFromJson:
                        mySqlCfgFromJson = gitVerInfoCfgFromJson["mySql"]
                    if not mySqlCfgFromJson:
                        白板.调试消息('mySqlCfgFromJson is None')
                    else:
                        if "host" in mySqlCfgFromJson:
                            self.mySqlCfg.host = mySqlCfgFromJson["host"]
                        if "user" in mySqlCfgFromJson:
                            self.mySqlCfg.user = mySqlCfgFromJson["user"]
                        if "pwd" in mySqlCfgFromJson:
                            self.mySqlCfg.pwd = mySqlCfgFromJson["pwd"]
                        if "db" in mySqlCfgFromJson:
                            self.mySqlCfg.db = mySqlCfgFromJson["db"]
                        if "table" in mySqlCfgFromJson:
                            self.mySqlCfg.table = mySqlCfgFromJson["table"]
                        if "charset" in mySqlCfgFromJson:
                            self.mySqlCfg.charset = mySqlCfgFromJson["charset"]

                    readRstFlg = True

        return readRstFlg

    def showInfo(self, 白板: 调试模板 = 调试模板()):
        白板.执行位置(self.__class__, self.showInfo)

        白板.准备表格()
        白板.添加一行('gitRePoDir:', self.gitRePoDir)
        白板.添加一行('productId:', 黄字(self.productId))
        白板.添加一行('majorBranchName:', self.majorBranchName)
        白板.添加一行('tagPrefix_major:', self.tagPrefix_major)
        白板.添加一行('minorBranchName:', self.minorBranchName)
        白板.添加一行('tagPrefix_minor:', self.tagPrefix_minor)
        白板.添加一行('anchorChar:', self.anchorChar)
        白板.添加一行('focusDoc_fileName:', self.focusDoc_fileName)
        白板.展示表格()

        self.mySqlCfg.showInfo(白板=白板.副本.缩进())

    @property
    def 副本(self):
        副本 = GitVerInfoCfg()

        副本.gitRePoDir = self.gitRePoDir
        副本.productId = self.productId
        副本.majorBranchName = self.majorBranchName
        副本.tagPrefix_major = self.tagPrefix_major
        副本.minorBranchName = self.minorBranchName
        副本.tagPrefix_minor = self.tagPrefix_minor
        副本.anchorChar = self.anchorChar
        副本.mySqlCfg = self.mySqlCfg.副本
        副本.focusDoc_fileName = self.focusDoc_fileName

        return 副本


# 定义一个 tagInfo 类
class TagInfo:
    def __init__(self,
                 dateTime: datetime = None,
                 name: str = None,
                 commit: str = None,
                 message: str = None,
                 author: str = None):
        self.dateTime: datetime = dateTime
        self.name: str = name
        self.commit: str = commit
        self.message: str = message
        self.author: str = author

    @property
    def valid(self):
        return self.dateTime and self.name and self.commit

    @property
    def 副本(self):
        副本 = TagInfo()

        副本.dateTime = self.dateTime
        副本.name = self.name
        副本.commit = self.commit
        副本.message = self.message
        副本.author = self.author

        return 副本

    def showInfo(self, 白板: 调试模板 = 调试模板()):
        白板.执行位置(self.__class__, self.showInfo)

        白板.准备表格()
        白板.添加一行('dateTime:', self.dateTime)
        白板.添加一行('name:', self.name)
        白板.添加一行('commit:', self.commit)
        白板.添加一行('message:', self.message)
        白板.添加一行('author:', self.author)
        白板.展示表格()


# 定义一个 commitInfo 类
class CommitInfo:
    def __init__(self,
                 currentBranchName: str = None,
                 branchNames: list[str] = None,
                 dateTime: datetime = None,
                 commit: str = None,
                 message: str = None,
                 author: str = None,
                 majorFlg: bool = False,
                 minorFlg: bool = False,
                 majorTagFlg: bool = False,
                 minorTagFlg: bool = False,
                 majorAnchorFlg: bool = False,
                 minorAnchorFlg: bool = False,
                 majorTagCoveringFlg: bool = False,
                 minorTagCoveringFlg: bool = False):
        # 附着在 commit 上的 tag 信息
        self.tags: dict[str, TagInfo] = {}
        # 附着在 commit 上的关键 tag 信息
        self.keyTag: TagInfo = TagInfo()

        self.currentBranchName: str = currentBranchName
        self.branchNames: list[str] = branchNames if isinstance(branchNames, list) else []
        self.dateTime: datetime = dateTime
        self.commit: str = commit
        self.message: str = message
        self.author: str = author

        self.majorFlg: bool = majorFlg
        self.minorFlg: bool = minorFlg
        self.majorTagFlg: bool = majorTagFlg
        self.minorTagFlg: bool = minorTagFlg
        self.majorAnchorFlg: bool = majorAnchorFlg
        self.minorAnchorFlg: bool = minorAnchorFlg
        self.majorTagCoveringFlg: bool = majorTagCoveringFlg
        self.minorTagCoveringFlg: bool = minorTagCoveringFlg

    @property
    def valid(self):
        return self.commit and self.dateTime

    def tagsCopyFrom(self, tags: dict[str, TagInfo] = None):
        self.tags = {}
        if isinstance(tags, dict):
            for tag in tags.values():
                self.tags[tag.name] = tag.副本

    def tagByPrefix(self, tagPrefix: str = None) -> TagInfo:
        thisTag = TagInfo()
        if tagPrefix:
            if self.tags:
                for tag in self.tags.values():
                    if re.findall(tagPrefix + r'\d', tag.name):
                        thisTag = tag.副本
                        break
        return thisTag

    @property
    def 副本(self):
        副本 = CommitInfo()

        副本.branchNames = copy.copy(self.branchNames)
        副本.currentBranchName = self.currentBranchName
        副本.dateTime = self.dateTime
        副本.commit = self.commit
        副本.message = self.message
        副本.author = self.author
        副本.majorFlg = self.majorFlg
        副本.minorFlg = self.minorFlg
        副本.majorTagFlg = self.majorTagFlg
        副本.minorTagFlg = self.minorTagFlg
        副本.majorAnchorFlg = self.majorAnchorFlg
        副本.minorAnchorFlg = self.minorAnchorFlg
        副本.majorTagCoveringFlg = self.majorTagCoveringFlg
        副本.minorTagCoveringFlg = self.minorTagCoveringFlg

        副本.tagsCopyFrom(self.tags)

        副本.keyTag = self.keyTag.副本

        return 副本

    def showInfo(self, 白板: 调试模板 = 调试模板()):
        白板.执行位置(self.__class__, self.showInfo)

        白板.准备表格()
        白板.添加一行('branchNames:', self.branchNames)
        白板.添加一行('currentBranchName:', self.currentBranchName)
        白板.添加一行('dateTime:', self.dateTime)
        白板.添加一行('commit:', self.commit)
        白板.添加一行('message:', self.message)
        白板.添加一行('author:', self.author)

        白板.添加一行('majorFlg:', self.majorFlg)
        白板.添加一行('minorFlg:', self.minorFlg)
        白板.添加一行('majorTagFlg:', self.majorTagFlg)
        白板.添加一行('minorTagFlg:', self.minorTagFlg)
        白板.添加一行('majorAnchorFlg:', self.majorAnchorFlg)
        白板.添加一行('minorAnchorFlg:', self.minorAnchorFlg)
        白板.添加一行('majorTagCoveringFlg:', self.majorTagCoveringFlg)
        白板.添加一行('minorTagCoveringFlg:', self.minorTagCoveringFlg)

        if not self.keyTag.valid:
            白板.添加一行('keyTag:', 红字('invalid'))
        if not self.tags:
            白板.添加一行('tags:', 红字('noTags'))
        白板.展示表格()

        if self.keyTag.valid:
            self.keyTag.showInfo(白板.副本.缩进())

        dbgForTagShow = 白板.副本.缩进()
        if self.tags:
            for tag in self.tags.values():
                tag.showInfo(dbgForTagShow)


# 定义一个 verInfo 类
class VerInfo:
    def __init__(self, cfg: GitVerInfoCfg = None):
        self.gitVerInfoCfg: GitVerInfoCfg = cfg if isinstance(cfg, GitVerInfoCfg) else GitVerInfoCfg()

        self.tagsList: list[CommitInfo] = []
        self.focusCommitList: list[str] = []

        self.__privateVersion: str = ''
        self.__publicVersion: str = ''

        self.majorCommit = CommitInfo()
        self.minorCommit = CommitInfo()
        self.currCommit = CommitInfo()
        self.baseCommit = CommitInfo()

        self.currCommitOffsetCnt: int = 0
        self.dirtyTimeStamp: str = ''

    @property
    def privateVersion(self) -> str:
        if self.__privateVersion and self.__publicVersion:
            return self.__privateVersion
        else:
            self.__verStrFormat()
            if self.__privateVersion and self.__publicVersion:
                return self.__privateVersion
            else:
                return ''

    @property
    def publicVersion(self) -> str:
        if self.__privateVersion and self.__publicVersion:
            return self.__publicVersion
        else:
            self.__verStrFormat()
            if self.__privateVersion and self.__publicVersion:
                return self.__publicVersion
            else:
                return ''

    def saveTagsList(self,
                     objPublished: str = None,
                     saveType_0Private_1Public_2Both: int = 2,
                     语义化提交日期: bool = False,
                     画板: 打印模板 = None) -> [bool, str]:
        画板 = 画板 if isinstance(画板, 打印模板) else 打印模板()
        画板.执行位置(self.__class__, self.saveTagsList)

        if isinstance(saveType_0Private_1Public_2Both, int) and saveType_0Private_1Public_2Both in [0, 1, 2]:
            saveType_0Private_1Public_2Both = saveType_0Private_1Public_2Both
        else:
            saveType_0Private_1Public_2Both = 2

        objPublished = str(objPublished if objPublished else '').strip()
        if not objPublished:
            画板.提示错误('objPublished is inValid')
            return False, ''

        tgtPath: str
        prefixForVerDoc: str
        if os.path.isfile(objPublished):
            tgtPath = os.path.dirname(os.path.abspath(objPublished))

            prefixForVerDoc = os.path.basename(objPublished)[::-1]
            suffixNameP = prefixForVerDoc.find('.')
            prefixForVerDoc = prefixForVerDoc[suffixNameP + 1:][::-1]
        elif os.path.isdir(objPublished):
            tgtPath = os.path.abspath(objPublished)

            prefixForVerDoc = os.path.basename(objPublished)
        else:
            画板.提示错误('所指定的发布文档无效:', objPublished)
            return False, ''

        tgtDoc = 'tagsList.ver'
        if prefixForVerDoc:
            tgtDoc = prefixForVerDoc + ' ' + tgtDoc

        if tgtPath.endswith(路径分隔符):
            tgtDoc = tgtPath + tgtDoc
        else:
            tgtDoc = tgtPath + 路径分隔符 + tgtDoc

        if os.path.isfile(tgtDoc):  # gitVer_TagsList.ver 已经存在，则尝试删除之
            try:
                os.remove(tgtDoc)
            except Exception as exp:
                画板.提示错误('在尝试删除 gitVer_TagsList.ver 文档时出现错误', exp)
                return False, ''

        # 创建一个新的 gitVer_TagsList.ver 文档，并往里面写入tagsList信息
        with open(tgtDoc, "w", encoding='utf-8') as f:
            self.gitVerInfoCfg.anchorChar = '*'
            版本历史表格 = self.__表格化版本历史(公私类型=saveType_0Private_1Public_2Both,
                                                 换行前导符='-',
                                                 彩色文本=False,
                                                 语义化提交日期=语义化提交日期,
                                                 画板=画板.副本.缩进())
            if 版本历史表格:
                表格 = 打印模板(打印头='', 特殊字符宽度字典={'・': 1})
                表格.准备表格(对齐控制串='lllrl')
                表格.表格列间距 = [2, 2, 2, 0]
                for 行元素 in 版本历史表格:
                    表格.添加一行(行元素)

                # 定义一个方法向文档中写入内容
                def 写入文档(行内容: str) -> None:
                    f.writelines(行内容 + '\n')

                # 展示表格内容,指定文档输出方法为 写入文档
                表格.展示表格(打印方法=写入文档)

        return True, tgtDoc

    def showInfo(self,
                 showType_0Private_1Public_2Both: int = 2,
                 copyed: bool = False,
                 语义化提交日期: bool = True,
                 画板: 打印模板 = None) -> None:
        画板 = 画板 if isinstance(画板, 打印模板) else 打印模板()
        画板.执行位置(self.__class__, self.showInfo)

        if isinstance(showType_0Private_1Public_2Both, int) and showType_0Private_1Public_2Both in [0, 1, 2]:
            showType_0Private_1Public_2Both = showType_0Private_1Public_2Both
        else:
            showType_0Private_1Public_2Both = 1

        版本历史表格 = self.__表格化版本历史(公私类型=showType_0Private_1Public_2Both,
                                             换行前导符=黄字('↩ ｜'),
                                             彩色文本=True,
                                             语义化提交日期=语义化提交日期,
                                             画板=画板.副本.缩进())
        版本历史表格无彩版 = []
        if copyed:
            版本历史表格 = self.__表格化版本历史(公私类型=showType_0Private_1Public_2Both,
                                                 换行前导符='↩ ｜',
                                                 彩色文本=True,
                                                 语义化提交日期=语义化提交日期,
                                                 画板=画板.副本.缩进())
        if 版本历史表格:
            if len(版本历史表格) == 1:
                画板.提示错误('无版本历史可展示')
                return None

            # 将历史表格放入打印模板进行打印输出
            画板.准备表格(对齐控制串='lllrl')
            画板.表格列间距 = [2, 2, 2, 0]

            # 把表格中的行添加到画板模板对象中
            for 行号 in range(len(版本历史表格)):
                if 行号 == 0:
                    # 如果这是第一行,则修改为青字,作为标题
                    画板.添加一行(版本历史表格[行号]).修饰行(青字)
                else:
                    画板.添加一行(版本历史表格[行号])

            if copyed and 版本历史表格无彩版:
                复制板 = 打印模板(False)
                复制板.打印头 = ' ' * len(画板.打印头)
                for 行号 in range(len(版本历史表格无彩版)):
                    复制板.添加一行(版本历史表格无彩版[行号])
                行字串列表: list[str] = []

                def 打印表格(行):
                    行字串列表.append(行)

                复制板.展示表格(打印方法=打印表格)

                if 行字串列表:
                    # 复制版本表
                    复制文本('\n'.join(行字串列表))

            # 准备展示表格的内容
            画板.消息('tags History is shown as:')
            画板.打印头 = ' ' * len(画板.打印头)
            画板.分隔线.修饰(红字).总长度(画板.表格宽度(), 适应窗口=True).展示()
            画板.展示表格()
            画板.分隔线.修饰(红字).总长度(画板.表格宽度(), 适应窗口=True).展示()

            # 打印输出版本号信息
            画板.准备表格()
            if showType_0Private_1Public_2Both in [0, 2]:
                画板.添加一行('privateVersion:', 黄字(self.privateVersion))
            if showType_0Private_1Public_2Both in [1, 2]:
                画板.添加一行('publicVersion:', 黄字(self.publicVersion))

            画板.添加一行('dirtyFlg:', 红字(True) if self.dirtyTimeStamp else 绿字(False))
            画板.展示表格()

    @property
    def 副本(self):
        副本 = VerInfo()

        副本.gitVerInfoCfg = self.gitVerInfoCfg.副本

        副本.tagsList = copy.deepcopy(self.tagsList)

        副本.majorCommit = self.majorCommit.副本
        副本.minorCommit = self.minorCommit.副本
        副本.currCommit = self.currCommit.副本
        副本.baseCommit = self.baseCommit.副本

        副本.currCommitOffsetCnt = self.currCommitOffsetCnt
        副本.dirtyTimeStamp = self.dirtyTimeStamp

        return 副本

    # region 私有方法
    def __verStrFormat(self, 白板: 调试模板 = 调试模板()):
        白板.执行位置(self.__class__, self.__verStrFormat)

        # 将版本各字段的信息进行合并
        self.__privateVersion = ''
        self.__publicVersion = ''
        if self.majorCommit.keyTag.valid:
            self.__privateVersion = self.__privateVersion + self.majorCommit.keyTag.name + '(' + self.majorCommit.commit[
                                                                                                 :7] + ').'
            self.__publicVersion = self.__publicVersion + self.majorCommit.keyTag.name + '.'
        else:
            self.__privateVersion = self.__privateVersion + '0.'
            self.__publicVersion = self.__publicVersion + '0.'
        if self.minorCommit.keyTag.valid:
            self.__privateVersion = self.__privateVersion + self.minorCommit.keyTag.name + '(' + self.minorCommit.commit[
                                                                                                 :7] + ').'
            self.__publicVersion = self.__publicVersion + self.minorCommit.keyTag.name + '.'
        else:
            self.__privateVersion = self.__privateVersion + '0.'
            self.__publicVersion = self.__publicVersion + '0.'
        if self.currCommit.valid and self.currCommit.currentBranchName:
            self.__privateVersion = self.__privateVersion + self.currCommit.currentBranchName + '.'
        else:
            self.__privateVersion = self.__privateVersion + '0.'
        if self.baseCommit.valid:
            if self.currCommitOffsetCnt > 0:
                self.__privateVersion = self.__privateVersion + self.baseCommit.commit[:7] + '(' + str(
                    self.currCommitOffsetCnt) + ').'
            else:
                self.__privateVersion = self.__privateVersion + self.baseCommit.commit[:7] + '.'
        else:
            self.__privateVersion = self.__privateVersion + '0.'
        if self.dirtyTimeStamp:
            self.__privateVersion = self.__privateVersion + self.dirtyTimeStamp + '.'
        else:
            self.__privateVersion = self.__privateVersion + '0.'

        # alph: 内测版
        # beta：公测版
        # stable：发行版
        # shit： 非法版本，指带有未提交代码的版本
        publicV = 'shit'
        if not self.dirtyTimeStamp:
            if self.majorCommit.keyTag.valid and not self.minorCommit.keyTag.valid and not self.currCommit.valid and not self.baseCommit.valid:
                # 如果只有主版本信息，则标记为 stable 版
                publicV = 'stable'
            else:
                if not self.minorCommit.keyTag.valid and self.currCommit.minorFlg and not self.baseCommit.valid:
                    # 如果子版本信息不存在，且当前节点在子分支上，且没有体现基节点信息，则标记为 beta 版
                    publicV = 'beta'
                elif self.minorCommit.keyTag.valid and not self.currCommit.valid and not self.baseCommit.valid:
                    # 如果子版本存在，但没有体现当前分支信息，且没有体现基节点信息，则标记为 beta 版
                    publicV = 'beta'
                elif self.currCommit.valid and not self.currCommit.majorFlg and not self.currCommit.minorFlg:
                    # 如果存在当前分支信息，但不在主分支上，也不在子分支上，则标记为 alph 版
                    publicV = 'alph'

        # 去除 self.__privateVersion 尾部多余的 .0. 或者 .0 或者 .
        while self.__privateVersion:
            if self.__privateVersion[-3:] == '.0.':
                self.__privateVersion = self.__privateVersion[:-3]
            elif self.__privateVersion[-2:] == '.0':
                self.__privateVersion = self.__privateVersion[:-2]
            elif self.__privateVersion[-1:] == '.':
                self.__privateVersion = self.__privateVersion[:-1]
            else:
                break

        # 生成一个 md5 值，来作为 self.__publicVersion 的唯一标记符
        md5 = hashlib.md5()
        md5.update(self.__privateVersion.encode('utf-8'))
        if publicV in ['shit', 'alph']:
            publicV = publicV + '(' + md5.hexdigest()[:11] + ')'
        self.__publicVersion = self.__publicVersion + publicV + '.'

        # 去除 self.__publicVersion 尾部多余的 .0. 或者 .0 或者 .
        while self.__publicVersion:
            if self.__publicVersion[-3:] == '.0.':
                self.__publicVersion = self.__publicVersion[:-3]
            elif self.__publicVersion[-2:] == '.0':
                self.__publicVersion = self.__publicVersion[:-2]
            elif self.__publicVersion[-1:] == '.':
                self.__publicVersion = self.__publicVersion[:-1]
            else:
                break

    def __表格化版本历史(self, 公私类型: int = 2,
                         换行前导符: str = '↩',
                         彩色文本: bool = True,
                         语义化提交日期: bool = True,
                         画板: 打印模板 = None) -> list[list[str]]:
        """
        根据指定的公私类型,将版本历史(tagsList)整理成一个 list[list[str]] 对象返回
        :param 公私类型: 0: 私有版本; 1: 公开版本; 2: 私有版本 + 公开版本
        :param 换行前导符: 如果commit/tag 消息存在多行,则换行前导符会出现在除第一行内容外的后续行前,提升阅读体验
        :param 彩色文本: 如果用于在 terminal 中输出,可以提供彩色文字支持
        :param 画板: 一个打印模板对象,提供消息的打印输出支持
        :return: list[list[str]] 对象
        """
        画板 = 画板 if isinstance(画板, 打印模板) else 打印模板()
        画板.执行位置(self.__class__, self.__表格化版本历史)

        公私类型 = 公私类型 if isinstance(公私类型, int) and 公私类型 in [0, 1, 2] else 1
        换行前导符 = 换行前导符.lstrip() if 换行前导符 else None
        if 换行前导符:
            if 换行前导符.endswith('  '):
                # 如果换行前导符以多个空格结尾,则处理成只有一个空格的样式
                换行前导符 = 换行前导符.rstrip() + ' '
        else:
            换行前导符 = ' '

        版本历史表: list[list[str]] = []

        # 如果 tagsList 内容是空的，则返回
        if not self.tagsList:
            画板.提示错误('tagsList 为空, 无版本历史可展示')
            return 版本历史表

        版本历史表.append(['版本标记', 'commit', '版本备注']) # 添加标题行
        提交人: str
        提交时间: str
        for idx in range(len(self.tagsList)).__reversed__():
            thisCmt: CommitInfo = self.tagsList[idx].副本

            版本标记前缀: str = '・'
            版本标记: str = ''
            标签名: str = ''
            if (not thisCmt.majorTagFlg) and thisCmt.minorTagFlg:
                # 如果不属于 majorTag，但属于 minorTag，则加 ' |-' 符号进行缩进处理
                版本标记前缀 = 版本标记前缀 + ' |-'
            elif not (thisCmt.majorTagFlg or thisCmt.minorTagFlg):
                # 如果不属于 majorTag，且不属于 minorTag， 则加 '  -' 符号进行缩进处理
                版本标记前缀 = 版本标记前缀 + '  -'
                if thisCmt.branchNames:
                    # 如果有 branchName 信息，则添加体现 branchName 信息
                    版本标记前缀 = f"{版本标记前缀}{','.join(sorted(set(thisCmt.branchNames)))}"
            if thisCmt.keyTag.valid:
                标签名 = thisCmt.keyTag.name
                if (thisCmt.majorAnchorFlg or thisCmt.minorAnchorFlg) and self.gitVerInfoCfg.anchorChar:
                    if 彩色文本:
                        版本标记 = f"{标签名}{黄字(self.gitVerInfoCfg.anchorChar)}"
                    else:
                        版本标记 = f"{标签名}{self.gitVerInfoCfg.anchorChar}"
                else:
                    版本标记 = f"{标签名}"
            版本标记 = 版本标记前缀 + 版本标记

            # 以 commitHash 为基准, 不展示 tagHash
            commitHash: str = thisCmt.commit[:7].strip()

            是否兴趣节点: bool = (thisCmt.commit in self.focusCommitList)

            需要高亮这条记录: bool = False
            需要添加这条记录: bool = False
            if thisCmt.keyTag.valid:
                需要添加这条记录 = True
            elif 是否兴趣节点:
                需要添加这条记录 = True
            elif 公私类型 in [0, 2]:  # 如果需要展示私有信息
                if not (thisCmt.majorTagCoveringFlg or thisCmt.minorTagCoveringFlg):
                    # 如果这条记录没有被主tag or 次tag覆盖,则需要添加并显示这条记录
                    需要添加这条记录 = True
                # 如果这样的分支被允许添加,则高亮之
                需要高亮这条记录 = 需要添加这条记录

            提交人 = ''
            提交时间 = ''
            if 需要高亮这条记录 or 是否兴趣节点:
                # 如果这条记录需要高亮,或者这是一个兴趣节点,则显示这条记录的提交信息
                if thisCmt.commit and thisCmt.author:
                    if 彩色文本:
                        提交人 = f'{黄字("/")}{thisCmt.author}'
                    else:
                        提交人 = f'/{thisCmt.author}'

                    if 语义化提交日期:
                        提交时间 = f'{语义日期模板(目标日期=thisCmt.dateTime, 上下午语义=True, 周序语义=True)}'
                    else:
                        提交时间 = thisCmt.dateTime.strftime("%Y%m%d %H:%M:%S")

            if 需要添加这条记录:
                tagMsgList: list[str]
                tagMsgStr: str = (thisCmt.keyTag.message if thisCmt.keyTag.valid else thisCmt.message).strip()
                if tagMsgStr:
                    tagMsgList = tagMsgStr.splitlines()
                else:
                    tagMsgList = ['no tag msg']
                tagMsgList = [消息.strip() for 消息 in tagMsgList if 消息.strip()]

                消息行数: int = len(tagMsgList)
                for 消息行号 in range(消息行数):
                    格式化消息: str = tagMsgList[消息行号]
                    if 是否兴趣节点:
                        if 彩色文本:
                            格式化消息 = 红字(格式化消息)
                        else:
                            格式化消息 = 格式化消息
                    elif 需要高亮这条记录:
                        if 彩色文本:
                            格式化消息 = 青字(格式化消息)
                        else:
                            格式化消息 = 格式化消息

                    if 消息行号 == 0:
                        if 提交时间 and 提交人:
                            版本历史表.append([版本标记,
                                               commitHash,
                                               浅黑字(''.join([提交时间,提交人])) + '\n' + 格式化消息])
                        else:
                            版本历史表.append([版本标记,
                                               commitHash,
                                               格式化消息])
                    else:
                        版本历史表.append(['',
                                           '',
                                           f"{换行前导符}{格式化消息}"])
                # 处理被覆盖的 tag 信息
                if 标签名 and 公私类型 in [0, 2] and thisCmt.tags:
                    for tag in thisCmt.tags.values():
                        if tag.name != 标签名:
                            tagMsgStr = tag.message.strip()
                            if tagMsgStr:
                                tagMsgList = tagMsgStr.splitlines()
                            else:
                                tagMsgList = ['no tag msg']
                            tagMsgList = [消息.strip() for 消息 in tagMsgList if 消息.strip()]

                            消息行数: int = len(tagMsgList)
                            for 消息行号 in range(消息行数):
                                消息: str = tagMsgList[消息行号]
                                if 消息行号 == 0:
                                    版本历史表.append([f"・{' ' * len(版本标记前缀)}-{tag.name}",
                                                       '-',
                                                       消息])
                                else:
                                    版本历史表.append(['',
                                                       '',
                                                       f"{换行前导符}{消息}"])

        return 版本历史表

    # endregion


# 定义一个gitRepoInfo 类，用于记录 gitRepo 的基础信息
class GitRepoInfo:
    def __init__(self, cfg: GitVerInfoCfg = None):
        self.gitVerInfoCfg: GitVerInfoCfg = cfg if isinstance(cfg, GitVerInfoCfg) else GitVerInfoCfg()
        self.verInfo: VerInfo = VerInfo(self.gitVerInfoCfg)
        self.repo: Repo = Repo()

        self.remotes: list[Remote] = []

        self.lgsMajorStr: str = ''
        self.lgsMinorStr: str = ''
        self.lgsCurrStr: str = ''

        # 主分支tag节点的commit列表
        self.majorTagCommitsList: list[CommitInfo] = []
        # 主分支上所包含的所有的commit hex 列表
        self.majorCommitsList: list[str] = []
        # 次分支tag节点的commit列表
        self.minorTagCommitsList: list[CommitInfo] = []
        # 次分支上所包含的所有的commit hex 列表
        self.minorCommitsList: list[str] = []
        self.currBranchCommitsList: list[CommitInfo] = []  # 这个list中的commitInfo，以时间顺序降序排列
        self.focusCommitHashsList: list[str] = []  # 这个list, 存放了兴趣 commits 的 Hashs 值序列

        self.dirtyOnCurrBranch: str = ''
        self.__allCommitsDic: dict[str, CommitInfo] = {}

    @property
    def valid(self) -> bool:
        默认标记: bool = False
        if self.gitVerInfoCfg.gitRePoDir:
            if self.gitVerInfoCfg.majorBranchName and self.gitVerInfoCfg.tagPrefix_major:
                默认标记 = True

        return 默认标记

    def showInfo(self, 白板: 调试模板 = 调试模板()):
        白板.执行位置(self.__class__, self.showInfo)

        白板.准备表格()
        白板.添加一行('path:', self.gitVerInfoCfg.gitRePoDir)
        白板.添加一行('majorBranchName:', self.gitVerInfoCfg.majorBranchName)
        白板.添加一行('tagPrefix_major:', self.gitVerInfoCfg.tagPrefix_major)
        白板.添加一行('minorBranchName:', self.gitVerInfoCfg.minorBranchName)
        白板.添加一行('tagPrefix_minor:', self.gitVerInfoCfg.tagPrefix_minor)
        白板.添加一行('dirtyOnCurrBranch:', self.dirtyOnCurrBranch)
        白板.展示表格()

        self.verInfo.showInfo()

    @property
    def 副本(self):
        副本 = GitRepoInfo()
        副本.gitVerInfoCfg = self.gitVerInfoCfg.副本
        副本.verInfo = self.verInfo.副本
        副本.remotes = copy.deepcopy(self.remotes)
        return 副本

    # 初始化 repo 仓库,并建立 __allCommitsDic 字典
    def repoInit(self, 画板: 打印模板 = None) -> bool:
        画板 = 画板 if isinstance(画板, 打印模板) else 打印模板()
        画板.执行位置(self.__class__, self.repoInit)

        # 如果 self.repo 已经实例化，则关掉之
        if self.repo:
            try:
                self.repo.close()
            except Exception as err:
                画板.提示调试错误('尝试关闭当前 git 仓库时，发生异常：', err)
                self.repo = None
                return False

        if not self.valid:
            画板.调试消息('repo 参数设置无效')
            self.showInfo(白板=画板.副本.缩进())
        else:
            if os.path.isdir(self.gitVerInfoCfg.gitRePoDir):
                if os.path.exists(self.gitVerInfoCfg.gitRePoDir):
                    try:
                        self.repo = Repo(self.gitVerInfoCfg.gitRePoDir)
                    except Exception as err:
                        画板.提示调试错误('初始化 git.Repo 实例时，发生异常：', err)
                        self.repo = None
                        return False
                else:
                    画板.提示调试错误('指定的 path 路径不存在：', self.gitVerInfoCfg.gitRePoDir)
            else:
                画板.提示调试错误('指定的 path 不是有效的路径：', self.gitVerInfoCfg.gitRePoDir)

        if not self.repo:
            画板.提示调试错误('repo 对象无效')
            return False
        else:
            # 读取 git 仓库的 remotes
            self.remotes = self.repo.remotes

            # 建立所有在当前分支上的tag的字典
            tmpTagsInfoDic: dict[str, dict[str, TagInfo]] = {}

            for tag in self.repo.tags:
                thisT = TagInfo()

                thisT.commit = tag.commit.hexsha
                thisT.name = tag.name
                if tag.tag:
                    thisT.dateTime = datetime.fromtimestamp(tag.tag.tagged_date)
                    thisT.message = tag.tag.message
                    thisT.author = tag.tag.tagger
                else:
                    thisT.dateTime = tag.commit.committed_datetime
                    thisT.message = tag.commit.message
                    thisT.author = tag.commit.author

                if thisT.commit not in tmpTagsInfoDic:
                    tmpTagsInfoDic[thisT.commit] = {}
                tmpTagsInfoDic[thisT.commit][thisT.name] = thisT

            # 建立所有commits的字典,以方便后续查询使用
            for cmt in self.repo.iter_commits('--all'):
                thisCmt = CommitInfo()

                thisCmt.commit = cmt.hexsha.strip()
                thisCmt.message = cmt.message
                thisCmt.dateTime = cmt.committed_datetime
                thisCmt.author = cmt.author

                if thisCmt.commit in tmpTagsInfoDic:
                    thisCmt.tagsCopyFrom(tmpTagsInfoDic[thisCmt.commit])

                self.__allCommitsDic[thisCmt.commit[:7]] = thisCmt

        if not self.__allCommitsDic:
            画板.提示调试错误('repo 实例中不存在有效的提交记录: ', self.gitVerInfoCfg.gitRePoDir)

        if self.repo and self.__allCommitsDic:
            return True
        else:
            return False

    def commit查询(self, commitSha: str) -> CommitInfo or None:
        if not commitSha or not self.__allCommitsDic:
            return None
        if commitSha in self.__allCommitsDic:
            return self.__allCommitsDic[commitSha].副本

    # 建立主分支 commit 序列
    def lgsMajorStrInit(self, 画板: 打印模板 = None) -> bool:
        画板 = 画板 if isinstance(画板, 打印模板) else 打印模板()
        画板.执行位置(self.__class__, self.lgsMajorStrInit)

        if self.lgsMajorStr:
            self.lgsMajorStr = ''
        if self.majorTagCommitsList:
            self.majorTagCommitsList = []
        if self.majorCommitsList:
            self.majorCommitsList = []

        if not self.gitVerInfoCfg.majorBranchName:
            return False

        if self.repo is not None:
            try:
                majorHeadPath = self.repo.heads[self.gitVerInfoCfg.majorBranchName].path
                if majorHeadPath:
                    self.lgsMajorStr = self.repo.git.log(majorHeadPath, "--date=iso", "--pretty=format:'%h %d DT%cd'")
                else:
                    画板.提示错误('指定的 major 分支不存在：', self.gitVerInfoCfg.majorBranchName)
                    return False
            except Exception as err:
                画板.提示调试错误('尝试读取 majorBranchName 分支上的记录时，发生异常：', err)

                self.lgsMajorStr = ''

        if self.lgsMajorStr:
            for line in self.lgsMajorStr.split("'"):
                line = line.strip()
                if line:
                    画板.调试消息(黄字(line))
                    # 解析 commit 信息
                    thisCmt = self.__commitInfoInOnelineLog(line, 画板=画板.副本.缩进())
                    if not thisCmt.commit:
                        continue
                    if self.gitVerInfoCfg.majorBranchName:
                        thisCmt.branchNames.append(self.gitVerInfoCfg.majorBranchName)
                    # 收集commit信息
                    self.majorCommitsList.append(thisCmt.commit)
                    if line.find('tag: ') > 0:  # 这一行中包含 tag 信息
                        thisTag = thisCmt.tagByPrefix(self.gitVerInfoCfg.tagPrefix_major)
                        if thisTag.valid:
                            thisCmt.keyTag = thisTag.副本
                            self.majorTagCommitsList.append(thisCmt)

        if self.lgsMajorStr and self.majorTagCommitsList:
            return True
        else:
            return False

    # 建立次分支 commit 序列
    def lgsMinorStrInit(self, 画板: 打印模板 = None) -> bool:
        画板 = 画板 if isinstance(画板, 打印模板) else 打印模板()
        画板.执行位置(self.__class__, self.lgsMinorStrInit)

        if self.lgsMinorStr:
            self.lgsMinorStr = ''
        if self.minorTagCommitsList:
            self.minorTagCommitsList = []
        if self.minorCommitsList:
            self.minorCommitsList = []

        if not self.gitVerInfoCfg.minorBranchName:
            return False

        if self.repo is not None:
            try:
                minorHeadPath = self.repo.heads[self.gitVerInfoCfg.minorBranchName].path
                if minorHeadPath:
                    self.lgsMinorStr = self.repo.git.log(minorHeadPath, "--date=iso", "--pretty=format:'%h %d DT%cd'")
                else:
                    画板.提示错误('指定的 minor 分支不存在：', self.gitVerInfoCfg.minorBranchName)
                    return False
            except Exception as err:
                画板.提示调试错误('尝试读取 minorBranchName 分支上的记录时，发生异常：', err)
                self.lgsMinorStr = ''

        if self.lgsMinorStr:
            for line in self.lgsMinorStr.split("'"):
                line = line.strip()
                if line:
                    画板.调试消息(黄字(line))
                    # 解析 commit 信息
                    thisCmt = self.__commitInfoInOnelineLog(line, 画板=画板.副本.缩进())
                    if not thisCmt.commit:
                        continue
                    if self.gitVerInfoCfg.minorBranchName:
                        thisCmt.branchNames.append(self.gitVerInfoCfg.minorBranchName)
                    # 收集commit信息
                    self.minorCommitsList.append(thisCmt.commit)
                    if line.find('tag: ') > 0:
                        thisTag = thisCmt.tagByPrefix(self.gitVerInfoCfg.tagPrefix_minor)
                        if thisTag.valid:
                            thisCmt.keyTag = thisTag.副本
                            self.minorTagCommitsList.append(thisCmt)

        if self.lgsMinorStr:
            # 此处，只要可以读到 minor 分支存在即可，不要求一定要有 minor 的 tag 存在
            return True
        else:
            return False

    # 建立当前分支 commit 序列
    def lgsCurrStrInit(self, 画板: 打印模板 = None) -> bool:
        画板 = 画板 if isinstance(画板, 打印模板) else 打印模板()
        画板.执行位置(self.__class__, self.lgsCurrStrInit)

        if self.lgsCurrStr:
            self.lgsCurrStr = ''
        if self.currBranchCommitsList:
            self.currBranchCommitsList = []

        if self.repo is not None:
            try:
                self.lgsCurrStr = self.repo.git.log("--date=iso", "--pretty=format:'%h %d DT%cd'")
                if self.repo.is_dirty():
                    self.dirtyOnCurrBranch = datetime.now().__format__("%Y%m%dT%H%M%S")
            except Exception as err:
                画板.提示调试错误('尝试读取 majorBranchName 分支上的记录时，发生异常：', err)

                self.lgsCurrStr = ''
                self.dirtyOnCurrBranch = ''

        if self.lgsCurrStr:
            branchName: str = ''
            for line in self.lgsCurrStr.split("'"):
                line = line.strip()
                if line:
                    画板.调试消息(黄字(line))
                    if line.find('DT') > 0:
                        thisCmt = self.__commitInfoInOnelineLog(line, 画板=画板.副本.缩进())
                        if thisCmt:
                            if line.find("HEAD ->") > 0:
                                branchName = thisCmt.currentBranchName
                            else:
                                thisCmt.currentBranchName = branchName

                            thisCmt.branchNames.append(thisCmt.currentBranchName)

                            if thisCmt.currentBranchName == self.gitVerInfoCfg.majorBranchName:
                                thisCmt.majorFlg = True
                            if thisCmt.currentBranchName == self.gitVerInfoCfg.minorBranchName:
                                thisCmt.minorFlg = True

                            self.currBranchCommitsList.append(thisCmt)

        if self.lgsCurrStr and self.currBranchCommitsList:
            return True
        else:
            return False

    # 建立兴趣分支 commit 序列
    def lgsFocusStrInit(self, 画板: 打印模板 = None) -> bool:
        画板 = 画板 if isinstance(画板, 打印模板) else 打印模板()
        画板.执行位置(self.__class__, self.lgsFocusStrInit)

        focusLgsStr: str = ''
        self.focusCommitHashsList = []

        if not self.gitVerInfoCfg.focusDoc_fileName:
            画板.提示调试错误('兴趣文档名称未指定')
        elif self.repo is not None:
            try:
                if self.gitVerInfoCfg.focusDoc_fileName:
                    focusLgsStr = self.repo.git.log("--date=iso",
                                                    "--pretty=format:'%h %d DT%cd'",
                                                    "--follow",
                                                    self.gitVerInfoCfg.focusDoc_fileName
                                                    )
            except Exception as err:
                画板.提示错误('尝试读取兴趣提交记录时，发生异常：', err)

        if focusLgsStr:
            for line in focusLgsStr.split("'"):
                line = line.strip()
                if line:
                    画板.调试消息(黄字(line))
                    if line.find('DT') > 0:
                        thisCmt: CommitInfo = self.__commitInfoInOnelineLog(line, 画板=画板.副本.缩进())
                        if thisCmt and thisCmt.commit:
                            if thisCmt.commit not in self.focusCommitHashsList:
                                self.focusCommitHashsList.append(thisCmt.commit)

        if focusLgsStr and self.focusCommitHashsList:
            return True
        else:
            return False

    # 根据给定的 git log 的参数,执行git log 命令,并返回读取结果
    def 读取提交记录(self, *log参数, 画板: 打印模板 = None) -> str:
        画板 = 画板 if isinstance(画板, 打印模板) else 打印模板()
        画板.执行位置(self.__class__, self.读取提交记录)

        读取结果: str = ''

        if self.repo is not None:
            try:
                读取结果 = self.repo.git.log(log参数)
            except Exception as err:
                画板.提示错误('尝试读取提交记录时，发生异常：', err)

        return 读取结果

    def commitsCoveredByTag(self, tagName: str, 画板: 打印模板 = None) -> list[str]:
        """
        根据指定的tagName,返回以当前所在分支,该tagName所在提交链上截止该tag位置(包含)的所有commit值,以list[str]格式返回
        :param tagName: 指定的tagName
        :param 画板: 提供打印输出的方法
        :return: list[commitId]
        """
        画板 = 画板 if isinstance(画板, 打印模板) else 打印模板()
        画板.执行位置(self.__class__, self.commitsCoveredByTag)

        tmpLgsStr: str = ''
        commitsList: list[str] = []

        # 处理入参
        tagName = str(tagName if tagName else '').strip()

        if not tagName:
            画板.提示调试错误('tagName 为空')
            return commitsList

        if self.repo is not None:
            try:
                tmpLgsStr = self.repo.git.log("--date=iso",
                                              "--pretty=format:'%h %d DT%cd'",
                                              tagName)
            except Exception as err:
                画板.提示调试错误(f'尝试读取指定tag[{tagName}]提交记录时,发生异常: {err}')
                tmpLgsStr = ''
            else:
                画板.调试消息('tmpLgsStr is: ', tmpLgsStr)

        if tmpLgsStr:
            for line in tmpLgsStr.split("'"):
                line = line.strip()
                if line:
                    画板.调试消息(黄字(line))
                    # 解析 commit 信息
                    thisCmt = self.__commitInfoInOnelineLog(line, 画板=画板.副本.缩进())
                    if thisCmt.commit:
                        commitsList.append(thisCmt.commit)
        return commitsList

    # region 私有方法定义
    # 从一个 log 记录中提取 commitHash
    def __commitHashInOnelineLog(self, logStr: str = None, 画板: 打印模板 = None):
        画板 = 画板 if isinstance(画板, 打印模板) else 打印模板()
        画板.执行位置(self.__class__, self.__commitHashInOnelineLog)

        thisHash: str = ''
        if logStr:
            hashEP = logStr.find(" ")
            if hashEP > 0:
                thisHash = logStr[:hashEP]

        if thisHash:
            画板.调试消息('commitHas is: ', thisHash)
        else:
            画板.提示调试错误('commitHas is None')

        return thisHash

    # 从一个 log 记录中提取 branchName
    def __branchNameInOnelineLog(self, logStr: str = None, 画板: 打印模板 = None):
        画板 = 画板 if isinstance(画板, 打印模板) else 打印模板()
        画板.执行位置(self.__class__, self.__branchNameInOnelineLog)

        brName = ''
        if logStr:
            brNameSP = logStr.find("HEAD -> ")
            if brNameSP > 0:
                subStr = logStr[brNameSP:]
                brNameEP = subStr.find(", ")
                if brNameEP < 0:
                    brNameEP = subStr.find(")")

                if brNameEP > 0:
                    brName = subStr[len("HEAD -> "):brNameEP]
        if brName:
            画板.调试消息('branchName is: ', brName)
        else:
            画板.提示调试错误('branchName is None')

        return brName

    # 从一个 log 记录中提取 commitInfo
    def __commitInfoInOnelineLog(self, logStr: str = None, 画板: 打印模板 = None) -> CommitInfo:
        画板 = 画板 if isinstance(画板, 打印模板) else 打印模板()
        画板.执行位置(self.__class__, self.__commitInfoInOnelineLog)

        thisCmt = CommitInfo()
        if logStr:
            commitHexsha = self.__commitHashInOnelineLog(logStr, 画板).strip()
            if commitHexsha in self.__allCommitsDic:
                thisCmt = self.__allCommitsDic[commitHexsha].副本
                thisCmt.currentBranchName = self.__branchNameInOnelineLog(logStr, 画板)
            else:
                画板.提示调试错误('the hexsha of ' + commitHexsha + ' is not contain in the self.__allCommitsDic')

        return thisCmt
    # endregion


# 定义一个 mysql 类， 用于记录 mysql 的基础信息
class MySqlInfo:
    def __init__(self, cfg: GitVerInfoCfg = None):
        self.mySqlCfg: MySqlCfg = cfg if isinstance(cfg, MySqlCfg) else MySqlCfg()
        self.__myCon = None
        self.__myCursor = None

    @property
    def valid(self) -> bool:
        return self.mySqlCfg.valid

    def tryBuildConnection(self, 白板: 调试模板 = 调试模板()) -> bool:
        if not self.valid:
            return False

        白板.执行位置(self.__class__, self.tryBuildConnection)

        try:
            self.__myCon = pymysql.connect(host=self.mySqlCfg.host,
                                           user=self.mySqlCfg.user,
                                           password=self.mySqlCfg.pwd,
                                           database=self.mySqlCfg.db,
                                           charset=self.mySqlCfg.charset)
            self.__myCursor = self.__myCon.cursor()
        except Exception as err:
            白板.提示错误('failed to connect to mysql: ', err)
            self.__myCon = None
            self.__myCursor = None

        if self.__myCon and self.__myCursor:
            return True
        else:
            return False

    def tryCloseConnection(self, 白板: 调试模板 = 调试模板()) -> bool:
        白板.执行位置(self.__class__, self.tryCloseConnection)

        if not self.__myCursor and not self.__myCon:
            return True

        operaionFlg = False
        try:
            self.__myCursor.close()
            self.__myCon.close()

            operaionFlg = True
        except Exception as err:
            白板.提示错误('failed to close the connection: ', err)
        finally:
            self.__myCursor = None
            self.__myCon = None

        return operaionFlg

    def tryExecuteSql(self, sqlStr: str = None, keepConnection: bool = False, 白板: 调试模板 = 调试模板()) -> bool:
        if not sqlStr:
            return False
        if not self.valid:
            return False

        白板.执行位置(self.__class__, self.tryExecuteSql)

        if not self.__myCon or not self.__myCursor:
            self.tryCloseConnection(白板)
            self.tryBuildConnection(白板)
        if not self.__myCon or not self.__myCursor:
            return False

        白板.调试消息('sql will be executed:', sqlStr)

        operationFlg = False
        try:
            self.__myCursor.execute(sqlStr)
            self.__myCursor.connection.commit()

            operationFlg = True
        except Exception as expErr:
            白板.提示错误('failed to executed sql', expErr)

        if not keepConnection:
            operationFlg = self.tryCloseConnection(白板)

        return operationFlg

    @property
    def 副本(self):
        副本 = MySqlInfo()

        副本.mySqlCfg = self.mySqlCfg.副本

        return 副本

    def showInfo(self, 白板: 调试模板 = 调试模板()):
        白板.执行位置(self.__class__, self.showInfo)

        self.mySqlCfg.showInfo(白板=白板)


# 定义一个 gitVerInfo 类
class GitVerInfo:
    def __init__(self, cfg: GitVerInfoCfg = None):
        cfg = cfg if isinstance(cfg, GitVerInfoCfg) else GitVerInfoCfg()
        self.repoInfo: GitRepoInfo = GitRepoInfo(cfg=cfg)
        self.mysqlInfo: MySqlInfo = MySqlInfo(cfg=cfg)

    # 展示对象信息
    def showInfo(self, 画板: 打印模板 = None):
        画板 = 画板 if isinstance(画板, 打印模板) else 打印模板()
        画板.执行位置(self.__class__, self.showInfo)
        self.repoInfo.showInfo(画板)
        self.mysqlInfo.showInfo(画板)

    # 获取对象中的 mysqlInfo 信息
    def getGitVerSqlInfo(self, 画板: 打印模板 = None) -> MySqlInfo:
        画板 = 画板 if isinstance(画板, 打印模板) else 打印模板()
        画板.执行位置(self.__class__, self.getGitVerSqlInfo)

        thisInfo = self.mysqlInfo.副本

        return thisInfo

    # 解析 gitRepo 中的 version 信息
    def versionDecode(self, 画板: 打印模板 = None) -> bool:
        画板 = 画板 if isinstance(画板, 打印模板) else 打印模板()
        画板.执行位置(self.__class__, self.versionDecode)

        # 初始化 repo 仓库实例
        if not self.repoInfo.repoInit(画板=画板.副本.缩进()):
            画板.提示错误('repo 仓库实例化失败')
            return False

        # 读取 repo 中的 major， minor， current 分支的log记录
        if not self.repoInfo.lgsMajorStrInit(画板=画板.副本.缩进()):
            画板.提示错误('repo 读取 major 分支的 log 记录失败')
            return False
        if not self.repoInfo.lgsMinorStrInit(画板=画板.副本.缩进()):
            画板.调试消息('repo 读取 minor 分支的 log 记录失败')
        if not self.repoInfo.lgsCurrStrInit(画板=画板.副本.缩进()):
            画板.提示错误('repo 读取 current 分支的 log 记录失败')
            return False
        elif not self.repoInfo.currBranchCommitsList:
            画板.提示错误('repo 读取 current 分支的提交记录为空')
            return False

        # 读取 repo 中的指定的兴趣文档的变动记录
        if not self.repoInfo.lgsFocusStrInit(画板=画板.副本.缩进()):
            画板.提示调试错误('repo 读取兴趣提交记录失败')
        elif self.repoInfo.focusCommitHashsList:
            self.repoInfo.verInfo.focusCommitList = []
            for cmt in self.repoInfo.focusCommitHashsList:
                self.repoInfo.verInfo.focusCommitList.append(cmt)

        # 将 major tags commit 和 minor tags commit 以及其对应的 idx 整理到 dict 中，以便判断是否命中
        commitHexshaList_majorTags: dict[str, int] = {}
        commitHexshaList_minorTags: dict[str, int] = {}
        for idx in range(len(self.repoInfo.majorTagCommitsList)):
            commitHexshaList_majorTags[self.repoInfo.majorTagCommitsList[idx].commit] = idx
        for idx in range(len(self.repoInfo.minorTagCommitsList)):
            commitHexshaList_minorTags[self.repoInfo.minorTagCommitsList[idx].commit] = idx

        keyTagCommit_major: CommitInfo = CommitInfo()  # 最新的/关键的一个major tag commit
        keyTagCommit_minor: CommitInfo = CommitInfo()  # 最新的/关键的一个minor tag commit

        # 当前分支的最近一个提交对象
        keyCommit_currBranch = self.repoInfo.currBranchCommitsList[0].副本
        if not keyCommit_currBranch.commit:
            画板.提示调试错误('当前提交点 commit 无效')

        # 当前分支路径上最新的一个 major tag，被认为是关键 major tag commit
        for idx in range(len(self.repoInfo.currBranchCommitsList)):
            thisHexsha = self.repoInfo.currBranchCommitsList[idx].commit
            if thisHexsha in commitHexshaList_majorTags:
                for subIdx in range(len(self.repoInfo.majorTagCommitsList)):
                    if thisHexsha == self.repoInfo.majorTagCommitsList[subIdx].commit:
                        keyTagCommit_major = self.repoInfo.majorTagCommitsList[subIdx].副本
                        break
                if keyTagCommit_major.valid:
                    break

        # 当前分支路径上最新的一个 minor tag，被认为是关键的 minor tag commit
        for idx in range(len(self.repoInfo.currBranchCommitsList)):
            thisHexsha = self.repoInfo.currBranchCommitsList[idx].commit
            if thisHexsha in commitHexshaList_minorTags:
                for subIdx in range(len(self.repoInfo.minorTagCommitsList)):
                    if thisHexsha == self.repoInfo.minorTagCommitsList[subIdx].commit:
                        keyTagCommit_minor = self.repoInfo.minorTagCommitsList[subIdx].副本
                        break
                if keyTagCommit_minor.valid:
                    break

        # 在判断 keyTagCommit_minor 有效性之前,以当前最新的 keyTagCommit_minor 节点,来整理commitList列表
        # 获取 keyTagCommit_major, keyTagCommit_minor 所合并的commitsList
        commitsCoveredByKeyTag_major: list[str] = []
        commitsCoveredByKeyTag_minor: list[str] = []
        if keyTagCommit_major.valid and keyTagCommit_major.keyTag.valid and keyTagCommit_major.keyTag.name:
            commitsCoveredByKeyTag_major = self.repoInfo.commitsCoveredByTag(tagName=keyTagCommit_major.keyTag.name,
                                                                             画板=画板.副本.缩进(''))
        if keyTagCommit_minor.valid and keyTagCommit_minor.keyTag.valid and keyTagCommit_minor.keyTag.name:
            commitsCoveredByKeyTag_minor = self.repoInfo.commitsCoveredByTag(tagName=keyTagCommit_minor.keyTag.name,
                                                                             画板=画板.副本.缩进(''))

        # 标记 keyCommit_currBranch 的 majorFlg and minorFlg
        if keyCommit_currBranch.commit in commitHexshaList_majorTags:
            keyCommit_currBranch.majorFlg = True
        if keyCommit_currBranch.commit in commitHexshaList_minorTags:
            keyCommit_currBranch.minorFlg = True

        # 如果 major 和 minor 的关键 commit 都存在，则需要进一步判断 minor commit 的有效性
        if keyTagCommit_major.commit and keyTagCommit_minor.commit and keyCommit_currBranch.commit:
            if keyCommit_currBranch.majorFlg:
                # 如果当前分支 keyCommit 位于 major tag 上，则忽略 minor 信息
                keyTagCommit_minor = CommitInfo()
            elif keyTagCommit_minor.commit in commitHexshaList_majorTags:
                # 如果 minor 与 major 共享了 commit，则忽略 minor 信息
                keyTagCommit_minor = CommitInfo()
            elif keyTagCommit_major.dateTime > keyTagCommit_minor.dateTime:
                # 如果 major commit 新于 minor commit，则忽略 minor 信息
                keyTagCommit_minor = CommitInfo()

        # 标记 keyCommit_currBranch 的 majorAnchorFlg and minorAnchorFlg
        if keyCommit_currBranch.commit == keyTagCommit_major.commit:
            keyCommit_currBranch.majorAnchorFlg = True
        if keyCommit_currBranch.commit == keyTagCommit_minor.commit:
            keyCommit_currBranch.minorAnchorFlg = True

        # 整理 verInfo 中 tagCommitsList 序列
        for idx in range(len(self.repoInfo.currBranchCommitsList)):
            commitId = self.repoInfo.currBranchCommitsList[idx].commit

            # 获取commit对象
            if commitId in commitHexshaList_majorTags:
                # 这个 commit 在 major 的 tags 列表中
                thisCmt = self.repoInfo.majorTagCommitsList[commitHexshaList_majorTags[commitId]].副本
            elif commitId in commitHexshaList_minorTags:
                # 这个 commit 在 minor 的 tags 列表中
                thisCmt = self.repoInfo.minorTagCommitsList[commitHexshaList_minorTags[commitId]].副本
            else:
                # 这个 commit 不在major 和 minor 的tag列表中
                thisCmt = self.repoInfo.currBranchCommitsList[idx].副本

            # 补充该commit的分支名称信息
            if thisCmt.commit in self.repoInfo.majorCommitsList:
                thisCmt.branchNames.append(self.repoInfo.gitVerInfoCfg.majorBranchName)
            if thisCmt.commit in self.repoInfo.minorCommitsList:
                thisCmt.branchNames.append(self.repoInfo.gitVerInfoCfg.minorBranchName)

            # 标记该commit是否被主tag or 次tab 所覆盖
            if thisCmt.commit in commitsCoveredByKeyTag_major:
                thisCmt.majorTagCoveringFlg = True
            if thisCmt.commit in commitsCoveredByKeyTag_minor:
                thisCmt.minorTagCoveringFlg = True

            # 标记该commit的 majorTagFlg, majorFlg, minorTagFlg, minorFlg, majorAnchorFlg, minorAnchorFlg 状态
            if thisCmt.valid:
                if thisCmt.commit in commitHexshaList_majorTags:
                    thisCmt.majorTagFlg = True
                    thisCmt.majorFlg = True
                if thisCmt.commit in commitHexshaList_minorTags:
                    thisCmt.minorTagFlg = True
                    thisCmt.minorFlg = True
                if thisCmt.commit == keyTagCommit_major.commit:
                    thisCmt.majorAnchorFlg = True
                if thisCmt.commit == keyTagCommit_minor.commit:
                    thisCmt.minorAnchorFlg = True

                self.repoInfo.verInfo.tagsList.append(thisCmt)

        # 打印 major minor currCommit 以及 dirty 状态的信息
        画板.准备表格()
        画板.添加一调试行('分类', '分支名称', 'commitHash', 'tagName', '提交日期')
        if keyTagCommit_major.valid:
            画板.添加一调试行('keyTag_major',
                              keyTagCommit_major.currentBranchName,
                              keyTagCommit_major.keyTag.commit[:7],
                              keyTagCommit_major.keyTag.name,
                              keyTagCommit_major.keyTag.dateTime)
        else:
            画板.添加一调试行('keyTag_major', 红字('inValid'))
        if keyTagCommit_minor.valid:
            画板.添加一调试行('keyTag_minor',
                              keyTagCommit_minor.currentBranchName,
                              keyTagCommit_minor.keyTag.commit[:7],
                              keyTagCommit_minor.keyTag.name,
                              keyTagCommit_minor.keyTag.dateTime)
        else:
            画板.添加一调试行('keyTag_minor', 红字('inValid'))

        画板.添加一调试行('keyCommit_currBranch',
                          keyCommit_currBranch.currentBranchName,
                          keyCommit_currBranch.commit[:7],
                          '',
                          keyTagCommit_major.dateTime)
        画板.添加一调试行('dirty', 红字('True') if self.repoInfo.dirtyOnCurrBranch else 绿字('cleared'))
        画板.展示表格()

        # 整理版本各字段的值
        self.repoInfo.verInfo.majorCommit = keyTagCommit_major.副本
        self.repoInfo.verInfo.minorCommit = keyTagCommit_minor.副本
        if not keyCommit_currBranch.majorFlg and not keyCommit_currBranch.minorFlg:
            # 如果当前分支不在major，也不在 minor 上，则需要体现当前分支的信息
            self.repoInfo.verInfo.currCommit = keyCommit_currBranch.副本
        if self.repoInfo.dirtyOnCurrBranch:
            self.repoInfo.verInfo.dirtyTimeStamp = self.repoInfo.dirtyOnCurrBranch

        if self.repoInfo.dirtyOnCurrBranch:
            self.repoInfo.verInfo.baseCommit = keyCommit_currBranch.副本
        else:
            showFlg = True
            if keyTagCommit_minor.keyTag.valid:
                if keyCommit_currBranch.minorFlg:
                    if keyCommit_currBranch.commit == self.repoInfo.verInfo.minorCommit.commit:
                        # 如果minor分支在，且当前 commit 在 minor tag 节点上，则不需要体现commit信息
                        showFlg = False
            elif keyTagCommit_major.keyTag.valid:
                if keyCommit_currBranch.majorFlg:
                    if keyCommit_currBranch.commit == self.repoInfo.verInfo.majorCommit.commit:
                        # 如果minor分支不在，但 major 分支在，且当前 commit 在 major tag
                        showFlg = False

            if showFlg:
                self.repoInfo.verInfo.baseCommit = keyCommit_currBranch.副本
        if self.repoInfo.verInfo.baseCommit.valid:
            # 如果 baseCommit 有效(需要展示)，则需要体现当前分支的信息
            self.repoInfo.verInfo.currCommit = keyCommit_currBranch.副本

        # 如果 baseCommit 存在，则需要计算其距离最近的 tag 的提交次数
        if self.repoInfo.verInfo.baseCommit.valid:
            self.repoInfo.verInfo.currCommitOffsetCnt = 0
            latestCmt = ''
            if keyTagCommit_minor.valid:
                latestCmt = keyTagCommit_minor.commit
            elif keyTagCommit_major.valid:
                latestCmt = keyTagCommit_major.commit
            if latestCmt:
                for idx in range(len(self.repoInfo.currBranchCommitsList)):
                    if latestCmt == self.repoInfo.currBranchCommitsList[idx].commit:
                        self.repoInfo.verInfo.currCommitOffsetCnt = idx
                        break

        # 版本字段信息整理完成，返回状态
        return True

    # 打印git log 日志
    def 打印日志(self,
                 日志数量: int = -1,
                 所有分支: bool = False,
                 语义化提交日期: bool = True,
                 画板: 打印模板 = None) -> bool:
        画板 = 画板 if isinstance(画板, 打印模板) else 打印模板()
        画板.执行位置(self.__class__, self.打印日志)

        特殊字称显示宽度字典: dict = 画板.特殊字符宽度字典
        特殊字称显示宽度字典['✔'] = 2
        画板.特殊字符宽度字典 = 特殊字称显示宽度字典

        # 定义一个日志记录类,用于管理一个日志记录的相关信息
        class 日志单元类:
            def __init__(self,
                         cmt: CommitInfo = None,
                         缩略哈稀: str = None,
                         分支图: str = None,
                         装饰: str = None):
                self.cmt: CommitInfo = cmt
                self.缩略哈稀: str = 缩略哈稀
                self.分支图: str = 分支图
                self.装饰: str = 装饰

            # region 访问器
            @property
            def 格式化装饰(self) -> str:
                if self.装饰:
                    装饰拆解: list[str] = []
                    for 部件 in self.装饰.split(','):
                        部件处理: str = 部件.strip()
                        if 部件处理:
                            装饰拆解.append(部件处理)

                    # 整理这个装饰中的本地分支信息
                    本地分支表: list[str] = []
                    for 部件 in 装饰拆解:
                        if 'HEAD ->' in 部件:  # 这是一个本地 HEAD
                            部件拆解: list[str] = 部件.split('->')
                            if 部件拆解 and len(部件拆解) > 1:
                                本地分支表.append(部件拆解[1].strip())
                        elif not ('/' in 部件 or 'tag' in 部件):
                            本地分支表.append(部件.strip())

                    if 本地分支表:
                        本地分支表.sort(key = len, reverse=True)

                    # 着色 HEAD 信息 和 tag 信息
                    装饰拆解第一步处理: list[str] = []
                    for 部件 in 装饰拆解:
                        if 'HEAD ->' in 部件:  # 这是一个包含 HEAD -> 的分支信息
                            装饰拆解第一步处理.append(绿字(部件.replace('HEAD ->', 青字('HEAD ->'))))
                        elif 'HEAD' in 部件:  # 这是一个包含 HEAD 的分支信息
                            装饰拆解第一步处理.append(部件.replace('HEAD', 青字('HEAD')))
                        elif 'tag' in 部件:  # 这是一个 tag 部件
                            装饰拆解第一步处理.append(黄字(部件))
                        else:
                            装饰拆解第一步处理.append(部件)

                    # 着色远程分支中的本地分支
                    远程分支中的本地分支: list[str] = []
                    装饰拆解第二步处理: list[str] = []
                    命中分支: str
                    for 部件 in 装饰拆解第一步处理:
                        if '/' in 部件:  # 这是一个远程分支部件
                            命中分支 = ''
                            for 分支名 in 本地分支表:
                                if 分支名 in 部件:  # 这个本地分支出现在了这个远程分支中
                                    命中分支 = 分支名
                                    break
                            if 命中分支:  # 如果这个远程分支存在于本地
                                远程分支中的本地分支.append(命中分支)
                                # 把部件中的 命中分支 着色为绿色
                                部件 = 部件.replace(命中分支, 绿字(命中分支))

                            # 把这个远程分支总体着色为红色
                            装饰拆解第二步处理.append(红字(部件))
                        else:  # 不是远程分支部件,不处理
                            装饰拆解第二步处理.append(部件)

                    # 去除远程分支中已经存在的本地分支,并将保留的本地分支着色为绿色
                    装饰拆解第三步处理: list[str] = []
                    for 部件 in 装饰拆解第二步处理:
                        if 部件 in 本地分支表:  # 这是一个本地分支
                            if 部件 not in 远程分支中的本地分支:  # 如果这个本地分支不存在于远程分支中
                                # 将这个本地分支着色为绿色
                                装饰拆解第三步处理.append(绿字(部件))
                        else:  # 这不是本地分支,不处理
                            装饰拆解第三步处理.append(部件)

                    if 装饰拆解第三步处理:
                        return '\n'.join(装饰拆解第三步处理)
                return ''

            @property
            def 格式化分支图(self) -> str:
                字符单元列表: list[str] = []

                # 定义一个类,用于记录符号的检测结果
                class 符号检测结果类:
                    def __init__(self):
                        self.存在: bool = False
                        self.位置: int = -1

                分支图: str = self.分支图.strip()
                if 分支图:
                    if '\x1b[' in 分支图:  # 这是一个可能带有颜色控制的字符串
                        字符列表: list[str] = list(分支图)
                        字符单元拆解: list[str] = []

                        启动符号: 符号检测结果类 = 符号检测结果类()
                        启动括号: 符号检测结果类 = 符号检测结果类()
                        结束括号: 符号检测结果类 = 符号检测结果类()
                        for 序号 in range(len(字符列表)):
                            if not 启动符号.存在 and '\x1b' == 字符列表[序号]:
                                # 检测到启动符号
                                if 启动符号.存在 and 启动括号.存在 and not 结束括号.存在:
                                    # 如果检测到了新的启动符号,但前一次检测过程中没有检测到结束括号,则补一个结束括号
                                    字符单元拆解.append('[m')
                                    字符单元列表.append(''.join(字符单元拆解))

                                启动符号.存在 = True
                                启动符号.位置 = 序号
                                字符单元拆解 = ['\033']

                                启动括号.存在 = False
                                结束括号.存在 = False
                                continue
                            if 启动符号.存在 and not 启动括号.存在 and '[' == 字符列表[序号]:
                                # 检测到启动括号
                                启动括号.存在 = True
                                启动括号.位置 = 序号
                                字符单元拆解.append('[')
                                continue
                            if 启动符号.存在 and 启动括号 and not 结束括号.存在 and '[' == 字符列表[序号]:
                                # 检测到结束括号
                                结束括号.存在 = True
                                结束括号.位置 = 序号
                                字符单元拆解.append('[')
                                continue
                            if 启动符号.存在 and 启动括号.存在 and 结束括号.存在 and 'm' == 字符列表[序号]:
                                # 检测到颜色控制字符结束
                                字符单元拆解.append('m')
                                字符单元列表.append(''.join(字符单元拆解))

                                启动符号.存在 = False
                                启动括号.存在 = False
                                结束括号.存在 = False
                                字符单元拆解 = []
                                continue

                            if 启动符号.存在 and 启动括号.存在:
                                # 如果这是一个颜色控制进行中的状态
                                字符单元拆解.append(字符列表[序号])
                            else:
                                # 如果尚未检测到启动符号,则这是一个孤立字符
                                字符单元列表.append(字符列表[序号])

                        if 启动符号.存在 and 启动括号.存在 and not 结束括号.存在 and 字符单元拆解:
                            # 如果检测已经结束,但前一次检测过程中没有检测到结束括号, 并且 字符单元拆解 中有待处理内容,则补一个结束括号
                            字符单元拆解.append('[m')
                            字符单元列表.append(''.join(字符单元拆解))
                    else:
                        字符单元列表 = list(分支图)

                if 字符单元列表:
                    字符单元列表 = [单元.replace('/', '↖').replace('\\', '↗') for 单元 in 字符单元列表]
                    分支图 = ''.join(字符单元列表.__reversed__())
                else:
                    分支图 = ''

                if 分支图 and self.装饰:
                    if 'HEAD ->' in self.装饰:
                        分支图 = 分支图.replace('*', 绿字('*'))

                return 分支图

            @property
            def 格式化消息(self) -> str:
                消息: str = ''
                if isinstance(self.cmt, CommitInfo) and self.cmt.message:
                    行列表: list[str] = [有效行 for 有效行 in [行.strip() for 行 in self.cmt.message.splitlines()]
                                             if 有效行]
                    消息行列表: list[str] = []
                    for 行 in 行列表:
                        if 行.startswith('feat'):
                            行 = 青字('feat') + 行[4:]
                        if 行.startswith('fix'):
                            行 = 浅红字('fix') + 行[3:]
                        if 行.startswith('docs'):
                            行 = 浅绿字('docs') + 行[4:]
                        if 行.startswith('style'):
                            行 = 浅黑字('style') + 行[6:]
                        if 行.startswith('refactor'):
                            行 = 浅洋红字('refactor') + 行[8:]
                        if 行.startswith('perf'):
                            行 = 绿字('perf') + 行[4:]
                        if 行.startswith('revert'):
                            行 = 红字('revert') + 行[6:]
                        if 行.startswith('test'):
                            行 = 浅黄字('test') + 行[4:]
                        消息行列表.append(行)
                    消息 = f"\n".join(消息行列表)
                return 消息
            @property
            def 语义化提交日期(self) -> str:
                if isinstance(self.cmt, CommitInfo):
                    return 语义日期模板(目标日期=self.cmt.dateTime, 上下午语义=True).__str__()
                return ''

            @property
            def 提交人(self) -> str:
                if isinstance(self.cmt, CommitInfo):
                    return str(self.cmt.author)
                return ''

            # endregion

        # 初始化 repo 仓库实例
        if not self.repoInfo.repoInit(画板=画板.副本.缩进()):
            画板.提示错误('repo 仓库实例化失败')
            return False

        # 读取 repo 中的 major， minor， current 分支的log记录
        if not self.repoInfo.lgsCurrStrInit(画板=画板.副本.缩进()):
            画板.提示错误('repo 读取 current 分支的 log 记录失败')
            return False

        提交记录: str
        if type(日志数量) in [int, float] and 日志数量 > 0:
            if 所有分支:
                提交记录 = self.repoInfo.读取提交记录(f'-{日志数量}',
                                                      '--all',
                                                      '--graph',
                                                      '--color',
                                                      '--decorate',
                                                      '--abbrev-commit',
                                                      "--pretty=format:↩%h↩%d",
                                                      画板=画板.副本.缩进())
            else:
                提交记录 = self.repoInfo.读取提交记录(f'-{日志数量}',
                                                      '--graph',
                                                      '--color',
                                                      '--decorate',
                                                      '--abbrev-commit',
                                                      "--pretty=format:↩%h↩%d",
                                                      画板=画板.副本.缩进())
        else:
            if 所有分支:
                提交记录 = self.repoInfo.读取提交记录('--graph',
                                                      '--all',
                                                      '--color',
                                                      '--decorate',
                                                      '--abbrev-commit',
                                                      "--pretty=format:↩%h↩%d", 画板=画板.副本.缩进())
            else:
                提交记录 = self.repoInfo.读取提交记录('--graph',
                                                      '--color',
                                                      '--decorate',
                                                      '--abbrev-commit',
                                                      "--pretty=format:↩%h↩%d", 画板=画板.副本.缩进())

        日志记录: list[日志单元类] = []
        if 提交记录:
            for 提交 in 提交记录.splitlines():
                提交分解: list[str] = 提交.split('↩')
                提交分解 = 提交分解 + ['', '', '']

                日志单元: 日志单元类 = 日志单元类()

                日志单元.分支图 = 提交分解[0]
                日志单元.缩略哈稀 = 提交分解[1]
                if 日志单元.缩略哈稀:
                    日志单元.cmt = self.repoInfo.commit查询(日志单元.缩略哈稀)

                if 提交分解[2]:
                    日志单元.装饰 = 提交分解[2].strip()
                    if 日志单元.装饰:
                        日志单元.装饰 = 日志单元.装饰[1:-1]

                日志记录.append(日志单元)

        if 日志记录:
            画板.打印头 = ''
            画板.缩进字符 = ''
            画板.准备表格(对齐控制串='rlllrl')
            画板.表格列间距 = [2, 2, 2, 2, 0, 0]

            记录数量: int = sum([1 for 记录 in 日志记录 if 记录.缩略哈稀])
            提示内容: str = f'共打印log记录 {青字(记录数量)} 条'
            if self.repoInfo.dirtyOnCurrBranch:
                提示内容 = f"""{提示内容}, {红字(f"❌ {黄字('-')}")}"""
            else:
                提示内容 = f"""{提示内容}, {绿字(f"✔ {黄字('-')}")}"""

            for 记录 in 日志记录:
                提交时间 = 记录.语义化提交日期 if 语义化提交日期 else (记录.cmt.dateTime.strftime("%Y%m%d %H:%M:%S") if 记录.cmt and 记录.cmt.dateTime else '')
                提交时刻 = 记录.cmt.dateTime.strftime("%Y%m%d %H:%M:%S") if 记录.cmt and 记录.cmt.dateTime else ''
                提交人 = 记录.提交人
                提交签名: str = ''.join([提交人, 绿字('/') if 提交时间 and 提交人 else '', 提交时间, 绿字('/') if 提交时刻 else '', 提交时刻 if 提交时刻 else ''])
                画板.添加一行(记录.格式化分支图,
                              记录.缩略哈稀 if 记录.缩略哈稀 else '',
                              记录.格式化装饰,
                              (记录.格式化消息 if 记录.格式化消息 else "") + ('\n' if 记录.格式化消息 and 提交签名 else '') + (浅黑字('by ' + 提交签名) if 提交签名 else ""))

            if 记录数量 > 30:
                画板.上下颠倒表格()
                画板.修饰列(0, 修饰方法=lambda 行: str(行).replace('↖', '/').replace('↗', '\\')).展示表格()
                画板.分隔线.提示内容(提示内容).总长度(画板.表格宽度(), 适应窗口=True).修饰(黄字).展示()
            else:
                画板.修饰列(0, 修饰方法=lambda 行: str(行).replace('↖', '\\').replace('↗', '/'))
                画板.分隔线.提示内容(提示内容).总长度(画板.表格宽度(), 适应窗口=True).修饰(黄字).展示()
                画板.展示表格()
        else:
            画板.提示错误('无 log 可打印')

    # 上传版本记录
    def uploadGitVer(self, eventName: str = '', eventComment: str = '', 白板: 调试模板 = 调试模板()) -> bool:
        白板.执行位置(self.__class__, self.uploadGitVer)

        if self.mysqlInfo.valid:
            # 构建一个 slq 语句，用来将指定的内容上传到 sql 中
            sqlStr = 'insert into ' + self.mysqlInfo.mySqlCfg.table + \
                     ' (productUUID, gitVerPublic, gitVerPrivate, eventName, eventComment,eventTime)' + \
                     ' values ("{}", "{}", "{}", "{}", "{}", "{}")'.format(self.repoInfo.gitVerInfoCfg.productId,
                                                                           self.repoInfo.verInfo.publicVersion,
                                                                           self.repoInfo.verInfo.privateVersion,
                                                                           eventName,
                                                                           eventComment,
                                                                           datetime.now())

            return self.mysqlInfo.tryExecuteSql(sqlStr=sqlStr, 白板=白板)
        else:
            return False


if __name__ == '__main__':
    画板: 打印模板 = 打印模板(True)
    画板.执行位置(__file__)

    # 定义入参对象,并解析命令行传入参数
    入参 = 命令行参数类()
    入参.解析入参(画板=画板.副本.缩进())

    if 画板.正在调试:
        if not 入参.productId:
            入参.productId = 'idForTest'
        if not 入参.gitRePoDir:
            入参.gitRePoDir = 'C:/Users/yaoyuan.dou/gitVersion'

    if 画板.正在调试:
        入参.展示(画板=画板.副本.缩进())

    git版本 = GitVerInfo(cfg=GitVerInfoCfg(gitRePoDir=入参.gitRePoDir,
                                           productId=入参.productId,
                                           majorBranchName=入参.majorBranchName,
                                           tagPrefix_major=入参.tagPrefix_major,
                                           minorBranchName=入参.minorBranchName,
                                           tagPrefix_minor=入参.tagPrefix_minor,
                                           focusDoc_fileName=入参.focusDoc_fileName,
                                           mySqlCfg=MySqlCfg(host=入参.gitVerSql_host,
                                                             user=入参.gitVerSql_user,
                                                             pwd=入参.gitVerSql_pwd,
                                                             db=入参.gitVerSql_db,
                                                             charset=入参.gitVerSql_charset,
                                                             table=入参.gitVerSql_table)))

    git版本.productId = 入参.productId

    解析成功 = git版本.versionDecode(画板=画板.副本.缩进())

    if 解析成功:
        git版本.showInfo(画板=画板.副本.缩进())
        if git版本.mysqlInfo.valid:
            git版本.uploadGitVer(eventName='test', eventComment='test for new module', 白板=画板.副本.缩进())
